package com.cetccloud.ap.proxy.channel.cossDomain;

import com.alibaba.fastjson.JSON;
import com.cetccloud.ap.config.ProxyConfig;
import com.cetccloud.ap.proxy.channel.Channel;
import com.cetccloud.ap.proxy.channel.cossDomain.model.CrossDomainModel;
import com.cetccloud.ap.proxy.channel.cossDomain.model.FileXmlModel;
import com.cetccloud.ap.proxy.channel.cossDomain.s30.S30Adapter;
import com.cetccloud.ap.proxy.channel.cossDomain.s30.S30Exception;
import com.cetccloud.ap.proxy.coord.Coord;
import com.cetccloud.ap.util.Constants;
import com.cetccloud.ap.util.exception.ApException;
import com.cetccloud.ap.util.trace.Trace;
import com.cetccloud.ap.util.trace.TraceLogger;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.Arrays;

/**
 * @ClassName CrossDomainChannel
 * @Description 跨网数据安全传输
 * @Author superz
 * @Date 2021/4/12 19:06
 * @Version 1.0
 **/
@Slf4j
@Component
public class CrossDomainChannel extends CrossDomainSupport implements Channel {

    @Autowired
    private ProxyConfig proxyConfig;

    @Autowired
    protected CacheManager cacheManager;

    @Autowired
    private S30Adapter s30Adapter;

    @Autowired
    @Qualifier("srcCoord")
    private Coord srcCoord;

    @Override
    public void message(String destZoneId, CrossDomainModel model) {
        log.info("### CrossDomainChannel message ### {}, [{}]", destZoneId, JSON.toJSONString(model));

        if (proxyConfig.isMock()) {
            log.info("### CrossDomainChannel message [mock state] ###");
            // 补充域、应用相关信息
            model.setSrcZoneId(s30Adapter.getS30Config().getProxy().getZoneId());
            model.setSrcAppId(s30Adapter.getS30Config().getProxy().getAppId());
            model.setDestZoneId(destZoneId);
            model.setDestAppId("destAppId#test");

            // 记录send日志
            TraceLogger.traceSend(Trace.success(), model);
            // 模拟接收到数据
            new Thread(() -> {
                receive(JSON.toJSONString(model));
            }).start();
            return;
        }
        // 补充域、应用相关信息
        model.setSrcZoneId(s30Adapter.getS30Config().getProxy().getZoneId());
        model.setSrcAppId(s30Adapter.getS30Config().getProxy().getAppId());
        // destZoneId -> destAppId
        String destAppId = s30Adapter.getProxyAppId(destZoneId);
        if (StringUtils.isBlank(destAppId)) {
            throw new ApException("目标域: [" + s30Adapter.getS30Config().getProxy().getAppName() + "]未部署或未注册");
        }
        model.setDestZoneId(destZoneId);
        model.setDestAppId(destAppId);
        // 调用：跨网数据安全传输，调用30s sdk
        try {
            String msgId = s30Adapter.message(destAppId, JSON.toJSONString(model));
            cacheManager.getCache(Constants.INVOKE_SRC_CACHE).put(msgId, model.getTransId());
            // 记录send日志
            TraceLogger.traceSend(Trace.success(), model);
        } catch (S30Exception e) {
            log.error("CrossDomainChannel.message error", e);
            TraceLogger.traceSend(Trace.error(e.getMessage()), model);
        }
    }

    @Override
    public void fileXml(String destZoneId, FileXmlModel model) {
        log.info("### CrossDomainChannel file ### {}, [{}]", destZoneId, JSON.toJSONString(model));

        if (proxyConfig.isMock()) {
            log.info("### CrossDomainChannel file [mock state] ###");
            // 补充域、应用相关信息
            model.setSrcZoneId(s30Adapter.getS30Config().getProxy().getZoneId());
            model.setSrcAppId(s30Adapter.getS30Config().getProxy().getAppId());
            model.setDestZoneId(destZoneId);
            String destAppId = "ds-b-754-test";
            model.setDestAppId(destAppId);

            try {
                s30Adapter.fileXml(destAppId, model.getFileName(), model.getFileSize());
                // 记录fileXml日志
                TraceLogger.traceFileXml(Trace.success(), model);
            } catch (S30Exception e) {
                log.error("CrossDomainChannel.fileXml error", e);
                TraceLogger.traceFileXml(Trace.error(e.getMessage()), model);
            }
            return;
        }
        // 补充域、应用相关信息
        model.setSrcZoneId(s30Adapter.getS30Config().getProxy().getZoneId());
        model.setSrcAppId(s30Adapter.getS30Config().getProxy().getAppId());
        // destZoneId -> destAppId
        String destAppId = s30Adapter.getProxyAppId(destZoneId);
        if (StringUtils.isBlank(destAppId)) {
            throw new ApException("目标域: [" + s30Adapter.getS30Config().getProxy().getAppName() + "]未部署或未注册");
        }
        model.setDestZoneId(destZoneId);
        model.setDestAppId(destAppId);

        try {
            s30Adapter.fileXml(Arrays.asList(destAppId), model.getFileName(), model.getFileSize());
            // 记录fileXml日志
            TraceLogger.traceFileXml(Trace.success(), model);
        } catch (S30Exception e) {
            log.error("CrossDomainChannel.fileXml error", e);
            TraceLogger.traceFileXml(Trace.error(e.getMessage()), model);
        }
    }

    @Override
    public void sendError(String msgId, String errorMessage) {
        String transId = cacheManager.getCache(Constants.INVOKE_SRC_CACHE).get(msgId, String.class);
        if (StringUtils.isNotBlank(transId)) {
            cacheManager.getCache(Constants.INVOKE_SRC_CACHE).evict(msgId);
            // 记录失败响应日志
            CrossDomainModel crossDomainModel = new CrossDomainModel();
            crossDomainModel.setTransId(transId);
            TraceLogger.traceResp(Trace.error(errorMessage), crossDomainModel);
            srcCoord.error(transId, errorMessage);
        }
    }

    @Override
    public void receive(String data) {
        log.info("### CrossDomainChannel receive ### [{}]", data);

        CrossDomainModel model = JSON.parseObject(data, CrossDomainModel.class);
        // todo 请求记录落库

        Mono<?> mono = crossDomainDispatcher.dispatch(model);
        mono.subscribe(v -> {
            // 记录recv日志
            TraceLogger.traceRecv(Trace.success(), model);
        }, t -> {
            // 记录recv日志
            TraceLogger.traceRecv(Trace.error(t.getMessage()), model);
        });
    }
}
